package com.incross.itracker;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.http.HttpResponse;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import android.webkit.WebView;
import android.widget.Toast;

import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.incross.itracker.common.MobileTrackingConfig;
import com.incross.itracker.http.BKConnectionManager;
import com.incross.itracker.tracking.TrackingInformations;
import com.incross.itracker.util.BKFileManager;
import com.incross.itracker.util.BKLog;
import com.incross.itracker.util.CommonUtils;


public class ITracker implements BKConnectionManager.RequestStateListener {
	
	// for purchase type
	public static final int PURCHASE_MONEY  = 100;
	public static final int PURCHASE_POINT  = 101;
	public static final int PURCHASE_COUPON = 102;
	public static final int PURCHASE_ETC    = 103;
	
	
	// request codes
	public static final int REQUEST_APP_START    = 200;
	public static final int REQUEST_APP_FINISH   = 201;
	public static final int REQUEST_PURCHASE     = 202;
	public static final int REQUEST_PAGE_CHANGE  = 203;
	public static final int REQUEST_WEB_TRACKING = 204;
	
	// for mode selection
	public static final int TRACKING_SERVER_TYPE_DEV = 210;
	public static final int TRACKING_SERVER_TYPE_REL = 211;
	
	// for http secure
	public static final int HTTP_MODE_SECURE = 300;
	public static final int HTTP_MODE_NORMAL = 301;
	
	public static final String UNKNOWN = "UNKNOWN";
	public static final String CHECKED = "CHECKED";
	
	// for handler
	public static final int HANDLE_GOOGLE_AD_INFO = 300;
	
	// key for save install referrer.
	public static final String KEY_INSTALL_REFERRER = "key_install_referrer";
	
	private int mTrackingServerType = TRACKING_SERVER_TYPE_REL;
	private static final String SAVE_FILE_PATH = Environment.getExternalStorageDirectory().toString() + "/Android/data/incross/itracker/";
	private static final String SAVE_FILE_NAME = "incross_adp_mat.txt";
	
	private static final String KEY_UDT_ID = "key_udt_id";
	
	// mWebTrackingPool and mWebViewPool must have same size.
	private ArrayList<String> mWebTrackingPool;
	private ArrayList<WebView> mWebViewPool;
	
	// base url
	private final String mBaseUrl = "://itracker.icast-ad.com:";
	private final String mBaseUrl_Dev = "https://112.169.59.43:9081/"/*"https://adpdev.kr.pe:9081/"*/;
	
	// App ID 는 DEV mode에서만 변경이 가능함.
	private String mAppId = "";
	
	private static Context mContext;
	private TrackingInformations mTrackingInfo;
	
	private BKConnectionManager mConnManager;
	
	private static ITracker mMobileTrackingInstance;
	
	private boolean mIsStartTagSended = false;
	
	private ArrayList<String> mPendingRequests;
	
	private long mStartTimeSec = -1;
	private long mEndTimeSec = -1;
	
	private String mSessionId = "UNKNOWN";
	private String mUdtId = "UNKNOWN";
	private String mAdId = "UNKNOWN";
	private String mInstallReferrer = "UNKNOWN";
	
	private int mHttpSecureMode = HTTP_MODE_SECURE;
	
	// for check Activity life cycle
	private boolean mIsOnStartCalled = false;
	private boolean mIsFirstActivity = true;
	
	private boolean mIsAdIdAvailable = false;
	
	// 20141203 디버그모드 셋팅관련 변수 추가.
	private static boolean mIsDebugMode = false;
	
	public static ITracker getInstances(Context context, String appId) {
		if(context == null) return null;
		mContext = context;
		if(mMobileTrackingInstance == null) {
			mMobileTrackingInstance = new ITracker(appId);
			BKLog.i("Create new mobile tracking instance");
		} else {
			BKLog.i("Mobile tracking instance already created!!");
		}

		return mMobileTrackingInstance;
	}
	
	private ITracker(String appId) {
		mAppId = appId;
		mTrackingInfo = new TrackingInformations(mContext);
		mConnManager = new BKConnectionManager(mContext);
		mConnManager.setOnConnectionStateListener(this);
		mPendingRequests = new ArrayList<String>();
		initializeWebTrackingPool();
		initializeVariables();
	}
	
	private void initializeWebTrackingPool() {
		if(mWebTrackingPool == null) {
			mWebTrackingPool = new ArrayList<String>();
		}
		
		if(mWebViewPool == null) {
			mWebViewPool = new ArrayList<WebView>();
		}
	}
	
	/*
	 * Lifecycle 관련 class 정의
	 * 정의된 Lifecycle을 호출해야 정상적인 트래킹 가능함.
	 * */
	public void onStart() {
		if(mIsFirstActivity) { // 앱이 처음 시작하는지의 여부를 판단함. onResume에서 App activating check를 하므로 여기서 해당 flag를 체크하면 새로운 세션인지 여부 판단가능.
			// 20141121 중간에 udt id 파일이 삭제되는 경우 신규로 받게될때 기존에 쓰레기 정보가 남아서 문제발생 소지가 생김. 그래서 매번 startTag 호출시에 보내는 것으로 변경함.
			String udtId = getUdtId();
			if(udtId != null && !udtId.equals("")) {
				mUdtId = udtId;
			} else {
				mUdtId = UNKNOWN;
			}
			BKLog.i("--------------- App ID : " + mAppId);
			BKLog.i("--------------- UDT ID : " + mUdtId);
			
			mIsFirstActivity = false;
			BKLog.i("New application session started!");
			// app이 처음 구동된다고 판단하고, 처음 구동될때 가지고 올 수 있는 정보들을 수집한다.
			mStartTimeSec = System.currentTimeMillis() / 1000;
			if(mInstallReferrer == UNKNOWN) {
				String installReferrer = CommonUtils.getStringPreference(mContext, KEY_INSTALL_REFERRER);
				if(installReferrer != null && !installReferrer.equals("")) {
					mInstallReferrer = installReferrer;
				}
			}
			
			// 20141120 상단의 Start당시에 뽑을 정보만 뽑아둔 후 ad info 추출작업 진행함.
			// 기존 작업은 start tag 전송이었으나 현재 바뀐 동작은 handler로 advertising info를 뽑은다음 정상이던 정상이 아니던 여부와 관계없이 처리 내용대로 ad id를 가져오는 작업을 한 후 sendStartTag전송코드 호출하는 것으로 변경.
			if(mContext != null) {
				new AsyncTask<Context, Integer, AdvertisingIdClient.Info>() {

					@Override
					protected Info doInBackground(Context... params) {
						BKLog.i("-- AD ID task start");
						Context context = params[0];
						if(context != null) {
							AdvertisingIdClient.Info info;
							try {
								info = AdvertisingIdClient.getAdvertisingIdInfo(context);
							} catch(IllegalStateException e) {
								info = null;
								e.printStackTrace();
							} catch(GooglePlayServicesRepairableException e) {
								info = null;
								e.printStackTrace();
							} catch(IOException e) {
								info = null;
								e.printStackTrace();
							} catch(GooglePlayServicesNotAvailableException e) {
								info = null;
								e.printStackTrace();
							} catch(Exception e) {
								info = null;
								e.printStackTrace();
							}
							if(info != null) return info;
						}
						return null;
					}
					
					@Override
					protected void onPostExecute(AdvertisingIdClient.Info result) {
						BKLog.i("-- AD ID task finished");
						adIdWorkFinished(result);
				    }
				}.execute(mContext);
			} else {
				// 20141120 : mContext가 null인 경우 AD ID는 unknown 처리하고 기존에 진행하려고 하던 start tag 전송동작 바로 실행
				mAdId = UNKNOWN;
				sendStartInfo();
			}
		} else {
			mIsOnStartCalled = true;
			BKLog.w("New Activity's onStart");
		}
	}
	
	// only use when debug mode.
	public void setAppId(String appId) {
		mAppId = "";
		if(appId != null && !appId.equals("") && MobileTrackingConfig.isDebugMode()) {
			mAppId = appId;
			BKLog.w("User inserted app id set. (" + mAppId + ")");
		}
	}
	
	public void onStop() {
		// onStop이 호출됐을 때, onStart 새로운 Activity 호출로 간주하고 계속 실행중으로 판단한다.
		if(mIsOnStartCalled) {
			// mIsAppActivating 으로 App active를 판단하고, 생명주기 체크용으로 만든 변수를 초기값 false로 세팅한다.
			mIsOnStartCalled = false;
			BKLog.d("Maybe new activity called");
		} else {
			// 종료로 판단하는 경우. 
			// 앱을 이탈하는 모든 경우를 종료로 생각한다.
			
			finishApplication();
			BKLog.d("Finish app");
		}
	}
	
	private void finishApplication() {
		BKLog.i("Application finish - ");
		sendEndInfo();
	}
	
	public void onConfigurationChanged() {
		// TODO : 혹시 있을지 모를 가로/세로 전환시 onCreate를 새로 부르는 곳이 있을지 모르므로..
		
	}
	
	
	private void initializeVariables() {
		// 변수 초기화
		mSessionId = UNKNOWN;
		mIsOnStartCalled = false;
		mIsStartTagSended = false;
		mIsFirstActivity = true;
		mStartTimeSec = -1;
		mEndTimeSec = -1;
		if(mPendingRequests != null) {
			mPendingRequests.clear();
		} else {
			mPendingRequests = new ArrayList<String>();
		}
	}
	
	/*
	 * other methods
	 * 
	 * */
	/**
	 * set server type. default server type is MobileTracking.TRACKING_SERVER_TYPE_DEV
	 * @param type : one of specified server type in MobileTracking class
	 * @return -1 if put wrong server type. 0 success to change
	 */
	public int setTrackingServerType(int type) {
		if(type == TRACKING_SERVER_TYPE_DEV || type == TRACKING_SERVER_TYPE_REL) {
			mTrackingServerType = type;
			return 0;
		}
		return -1;
	}
	
	/**
	 * set secure mode.
	 * @param secureMode : one of secure mode specified in ITracker.
	 * @return -1 if unknown secure mode. otherwise returns 0.
	 */
	public int setHttpSecureMode(int secureMode) {
		if(secureMode != HTTP_MODE_SECURE && secureMode != HTTP_MODE_NORMAL) {
			return -1;
		} else {
			mHttpSecureMode = secureMode;
		}
		
		return 0;
	}
	
	/**
	 * set debug mode.
	 * @param enabled : true is set debug mode. default value is false
	 */
	public static void setDebugModeEnabled(boolean enabled) {
		mIsDebugMode = enabled;
	}
	
	private String getBaseUrl(int requestType) {
		String baseUrl = "";
		
		if(mTrackingServerType == TRACKING_SERVER_TYPE_REL) {
			if(mHttpSecureMode == HTTP_MODE_SECURE) {
				baseUrl = "https" + mBaseUrl + "19081/";
			} else {
				baseUrl = "http" + mBaseUrl + "18081/";
			}
		} else {
			baseUrl = mBaseUrl_Dev;
		}
		
		switch(requestType) {
			case REQUEST_APP_START:
				baseUrl += "start";
				break;
				
			case REQUEST_PAGE_CHANGE:
				baseUrl += "page";
				break;
				
			case REQUEST_PURCHASE:
				baseUrl += "purchase";
				break;
				
			case REQUEST_APP_FINISH:
				baseUrl += "end";
				break;
				
			case REQUEST_WEB_TRACKING: // 따로 정의된 바가 없으므로 pageTracking과 동일 취급
				baseUrl += "page";
				break;
		}
		return baseUrl;
	}
	
	private void sendPendingRequest() {
		if(mPendingRequests != null && mPendingRequests.size() > 0) {
			for(int i = 0; i < mPendingRequests.size(); i++) {
				String url = mPendingRequests.get(i);
				int request = extractRequest(url);
				if(request != -1) {
					BKLog.w("send pending request : " + mPendingRequests.get(i));
					mConnManager.sendRequest(request, checkUnknownValues(mPendingRequests.get(i)), BKConnectionManager.HTTP_POST);
				}
			}
		}
	}
	
	private int extractRequest(String url) {
		int request = -1;
		if(url != null && !url.equals("")) {
			String[] splitString = url.split("\\?");
			String baseUrl = "";
			if(splitString != null && splitString.length != 0) {
				baseUrl = splitString[0];
				String[] splitString2 = baseUrl.split("\\/");
				
				if(splitString2 != null && splitString2.length != 0) {
					String target = splitString2[splitString2.length - 1];
					
					if(target.equals("page")) {
						request = REQUEST_PAGE_CHANGE;
					} else if(target.equals("purchase")) {
						request = REQUEST_PURCHASE;
					} else if(target.equals("end")) {
						request = REQUEST_APP_FINISH;
					}
				}
			}
		}
		return request;
	}
	
	private String checkUnknownValues(String url) {
		String result = "";
		ArrayList<String> splitList = new ArrayList<String>();
		boolean isUnknownSsidFound = false;
		boolean isUnknownAdidFound = false;
		boolean isUnknownUdtidFound = false;
		boolean isUnknownInstallreferrerFound = false;
		
		try {
			if(url != null && !url.equals("")) {
				String[] splitString = url.split("\\&");
				if(splitString != null && splitString.length > 0) {
					// 20141124 기존에는 리스트에서 바로 제거하고 추가를 했는데, ArrayList는 제거시 중간에 빈 공간이 사라져 버려서 순번이 흐트러져 잘못되는 경우가 발견됐다. 그래서 새로운  arraylist를 만든 후 바뀐것은 바꿔서 추가하는 방식으로 변경함.
					ArrayList<String> resultList = new ArrayList<String>();
					// 1. arraylist에 split한 string 추가.. 2. arraylist 체크하여 session id가 포함된 부분이 있는지 체크 3. session id와 = 을 구분하여 unknown여부 확인 4. unknown이면 저장된 놈과 바꾸어 조립
					for(int i = 0; i < splitString.length; i++) {
						BKLog.d(i + ") put into ArrayList : " + splitString[i]);
						splitList.add(splitString[i]);
					}
					
					for(int i = 0; i < splitList.size(); i++) {
						String target = (String) splitList.get(i);
						
						if(target.contains("session_id")) {
							String[] spTarget = target.split("\\=");
							if(spTarget != null && spTarget.length > 0) {
								String sessionId = spTarget[1];
								if(!sessionId.equals(mSessionId)) {
									resultList.add("session_id=" + mSessionId);
									isUnknownSsidFound = true;
									BKLog.i(i + ", unknown session id replaced : " + mSessionId);
								}
							}
						} else if(target.contains("ad_id")) {
							String[] spTarget = target.split("\\=");
							if(spTarget != null && spTarget.length > 0) {
								String adId = spTarget[1];
								if(adId != null && adId.equals("UNKNOWN") && mIsAdIdAvailable) { // 저장된 ADID가 unknown이며 adid가 설정될때 true값을 가지는 변수가 true인 경우에 adid를 대체
									resultList.add("ad_id=" + mAdId);
									isUnknownAdidFound = true;
									BKLog.i(i + ", unknown ad id replaced : " + mAdId);
								} else {
									if(adId != null && !adId.equals("") && !mAdId.equals(adId)) { // 위의 조건이 맞지 않는 경우 사용자가 ADID 사용을 거부하기 위해 checked 된 경우는 따로 CHECKED로 대체하여 보냄
										String _adId = "ad_id=" + mAdId;
										resultList.add(_adId);
										isUnknownAdidFound = true;
										BKLog.i(i + ", user doesn't want collect ad id. replace unknown to checked : " + _adId);
									}
								}
							}
						} else if(target.contains("udt_id")) {
							String[] spTarget = target.split("\\=");
							if(spTarget != null && spTarget.length > 0) {
								String udtId = spTarget[1];
								if(udtId != null && !udtId.equals(mUdtId)) {
									resultList.add("udt_id=" + mUdtId);
									isUnknownUdtidFound = true;
									BKLog.i("unknown udt id replaced : " + mUdtId);
								} else {
									resultList.add(target);
								}
							}
						} else if(target.contains("install_referrer")) { // 20141121 모든 TAG를 모든 요청에 보내도록 했으므로, pending request에 추가함.
							BKLog.i("install_referrer found");
							checkInstallReferrer();
							String[] spTarget = target.split("\\=");
							if(spTarget != null && spTarget.length > 0) {
								String referrer = spTarget[1];
								if(referrer != null && !referrer.equals(mInstallReferrer)) {
									resultList.add("install_referrer=" + mInstallReferrer);
									isUnknownInstallreferrerFound = true;
									BKLog.i(i + ", unknown install referrer replaced : " + mInstallReferrer);
								} else {
									resultList.add(target);
									BKLog.i(i + "install referrer not changed : " + target);
								}
							}
						} else { // 추가할 수 있는 항목이 없는 경우 그대로 복사.
							BKLog.i(i + ", nothing is changed : " + target);
							resultList.add(target);
						}
					}
					
					if(isUnknownSsidFound || isUnknownAdidFound || isUnknownUdtidFound || isUnknownInstallreferrerFound) {
						for(int i = 0; i < resultList.size(); i++) {
							result += resultList.get(i) + (i != resultList.size() - 1? "&": "");
						}
					} else {
						result = url;
					}
					BKLog.i("result : " + result);
				}
			}
		} catch(Exception e) {
			e.printStackTrace();
			result = url;
		}
		
		return result;
	}
	
	private boolean saveUdtId(String udtId) {
		if(udtId == null || udtId.equals("") || udtId.equals("UNKNOWN")) return false;
		BKLog.e("Save udt id.. dir : " + Environment.getExternalStorageDirectory());
		BKFileManager fManager = new BKFileManager();
		
		try {
			JSONObject obj = new JSONObject();
			obj.put(KEY_UDT_ID, udtId);
			
			String jsonString = obj.toString();
			if(fManager.saveToTextFile(SAVE_FILE_PATH, SAVE_FILE_NAME, jsonString)) {
				BKLog.i("Save UDT ID : " + jsonString);
				return true;
			} else {
				return false;
			}
		} catch(JSONException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	private String getUdtId() {
		String fileString = "";
		String result = "";
		
		BKFileManager fManager = new BKFileManager();
		BKLog.i("File Path : " + SAVE_FILE_PATH + SAVE_FILE_NAME);
		fileString = fManager.readTextFileToString(SAVE_FILE_PATH + SAVE_FILE_NAME);
		
		if(fileString == null || fileString.equals("")) {
			BKLog.w("Saved uniq id not found");
			fileString = "";
		} else {
			BKLog.e("saved file String : " + fileString);
			try {
				JSONObject obj = new JSONObject(fileString);
				result = obj.getString(KEY_UDT_ID);
			} catch(JSONException e) {
				result = "";
				e.printStackTrace();
			}
		}
		return result;
	}
	
	private void checkInstallReferrer() {
		if(mInstallReferrer == UNKNOWN || mInstallReferrer == null || mInstallReferrer.equals("")) {
			String referrer = CommonUtils.getStringPreference(mContext, KEY_INSTALL_REFERRER);
			if(referrer != null && !referrer.equals("")) {
				mInstallReferrer = referrer;
			}
		}
	}
	
	private void adIdWorkFinished(AdvertisingIdClient.Info adInfo) {
		if(adInfo != null) {
			if(adInfo.isLimitAdTrackingEnabled()) {
				BKLog.e("isLimitAdTrackingEnabled checked");
				mAdId = CHECKED;
			} else {
				String adId = adInfo.getId();
				if(adId != null && !adId.equals("")) {
					mAdId = adId;
					mIsAdIdAvailable = true;
					BKLog.i("Ad ID : " + mAdId);
				} else {
					BKLog.e("get ad id failure");
					mAdId = UNKNOWN;
				}
			}
		} else {
			mAdId = UNKNOWN;
		}
		
		sendStartInfo();
		
	}
	
	/*
	 * Action 관련 class 정의
	 * 모든 요청에는 서버에서 발급받은 Session ID, package name, udid 를 넣어서 보내는 것으로..
	 * */
	
	/**
	 * Call this when any case of pay.
	 * @param productId : product's identification.
	 */
	public void sendPurchaseInfo(String productId, String price) {
		if(productId == null || productId.equals("")) {
			productId = "UNKNOWN";
		}
		
		if(price == null || price.equals("")) {
			price = "UNKNOWN";
		}
		
		String url = addDefaultParam(REQUEST_PURCHASE);
		
		url = url + "&product_id=" + productId + "&product_price=" + price;
		// 20140624 서버팀 요청으로 구매시 유입경로, 국가, 디바이스네임, 통신사 추가하는 것으로 수정
		// 20141208 모든요청에 모든 정보 보내기로 수정하여 구매시 서버요청으로 추가하던 항목 추가구문 삭제.
		
		if(mConnManager == null) {
			mConnManager = new BKConnectionManager(mContext);
			mConnManager.setOnConnectionStateListener(this);
		}
		if(mIsStartTagSended) {
			mConnManager.sendRequest(REQUEST_PURCHASE, url, BKConnectionManager.HTTP_POST);
		} else {
			if(mPendingRequests == null) {
				mPendingRequests = new ArrayList<String>();
			}
			mPendingRequests.add(url);
		}
		
	}
	
	public void sendPageInfo(String pageId) {
		// TODO : page별 트래킹용으로 페이지가 변경될 때 또는 특정 Activity별 방문경로 추적시에 넣는다.
		String url = addDefaultParam(REQUEST_PAGE_CHANGE);
		url += "&page_id=" + pageId;
		if(mIsStartTagSended) {
			mConnManager.sendRequest(REQUEST_PAGE_CHANGE, url, BKConnectionManager.HTTP_POST);
			Toast.makeText(mContext, "Page tracking tag 전송.(PageId : " + pageId + ")", Toast.LENGTH_SHORT).show();
		} else {
			mPendingRequests.add(url);
			BKLog.d("pending page info send - start tag not sended");
		}
	}
	
	/**
	 * call on shouldOverrideUrlLoading(WebView view, String url)
	 * @param webView 
	 * @param url
	 */
	public void sendWebTrackInfo(WebView webView, String url) {
		// TODO : 리다이렉션으로 인해 중복으로 들어가는 url에 대한 처리 추가.. 어떻게 할 것인가...
		// 1. 풀의 초기화 여부 확인 2. 풀이 비어있는지 여부 확인 3. 풀이 비어있으면 삽입 후 100msec뒤에 전송하는 것으로 
		if(webView == null || url == null) return;
		BKLog.e("################## sendWebTrackingInfo called ########################");
		Log.e("BenTag", "WebView url = " + url);
		BKLog.e("Original Url : " + webView.getOriginalUrl());
		
		String encodedUrl = "";
		try {
			encodedUrl = URLEncoder.encode(url, "UTF-8");
			BKLog.e("encoded url : " + encodedUrl);
		} catch(Exception e) {
			e.printStackTrace();
		}
		
		// step 1 풀의 초기화 여부 확인
		if(mWebTrackingPool == null || mWebViewPool == null) initializeWebTrackingPool();
		
		boolean isSameOriginalUrlSaved = false; // WebView
		// step 2 풀이 비어있는지 확인하여 풀이 비어있으면 바로 넣고 아니면 비교
		if(mWebTrackingPool.size() == 0 && mWebViewPool.size() == 0) {
			BKLog.d("- Pool is empty. save web tracking information into web tracking pool");
			mWebTrackingPool.add(encodedUrl);
			mWebViewPool.add(webView);
		} else {
			// step 3 풀이 비어있지 않는 경우 real 주소 비교.
			BKLog.d("- Pool is Not empty. Check start whether same original url saved or not.");
			for(int i = 0; i < mWebViewPool.size(); i++) {
				WebView wv = (WebView) mWebViewPool.get(i);
				if(wv.getOriginalUrl().equals(webView.getOriginalUrl())) {
					isSameOriginalUrlSaved = true;
					break;
				}
			}
			BKLog.d("- pool check end");
			
			if(!isSameOriginalUrlSaved) {
				BKLog.d("- Not found same saved value.. add into pool.");
				mWebTrackingPool.add(encodedUrl);
				mWebViewPool.add(webView);
			}
		}
		
		// step 4. 풀의 사이즈를 보고, 풀이 비어있지 않으며, 동일 original url을 발견하지 않은경우
		if(!isSameOriginalUrlSaved && mWebTrackingPool.size() != 0) {
			BKLog.d("Make timer task. and schedule with 300msec delay. - " + mWebTrackingPool.get(0));
			Timer timer = new Timer();
			TimerTask task = new TimerTask() {

				@Override
				public void run() {
					BKLog.d("300msec passed. prepare to send web page tracking.");
					String webTracking = mWebTrackingPool.get(0);
					if(webTracking != null) {
						BKLog.d("Send Web tracking info. - " + webTracking);
						String url = addDefaultParam(REQUEST_WEB_TRACKING) + "&page_id=" + webTracking;
						if(mIsStartTagSended) {
							mConnManager.sendRequest(REQUEST_WEB_TRACKING, url, BKConnectionManager.HTTP_POST);
						} else {
							if(mPendingRequests == null) {
								mPendingRequests = new ArrayList<String>();
							}
							mPendingRequests.add(url);
						}
						
						//////////////////////////////////////////////////////
						BKLog.e("Pool before remove.");
						for(int i = 0; i < mWebTrackingPool.size(); i++) {
							BKLog.d(i + " pool : " + mWebTrackingPool.get(i));
						}
						//////////////////////////////////////////////////////
						// remove pool
						mWebTrackingPool.remove(0);
						mWebViewPool.remove(0);
						
						//////////////////////////////////////////////////////
						BKLog.e("Pool after remove.");
						for(int i = 0; i < mWebTrackingPool.size(); i++) {
							BKLog.d(i + " pool : " + mWebTrackingPool.get(i));
						}
						//////////////////////////////////////////////////////
					} else {
						BKLog.e("saved value is null - wrong case ");
					}
				}
			};
			timer.schedule(task, 300);
		} else {
			if(isSameOriginalUrlSaved) {
				BKLog.i("Same original url is saved. do nothing..");
			}
			
			if(mWebTrackingPool.size() == 0) {
				BKLog.e("Pool size is zero");
			}
		}
	}
	
	private void sendStartInfo() {
		BKLog.w("Send start info.");
		// 20141118 Google Advertising ID를 추출하기 위해서는 시간이 걸린다. 구글 Advertising ID를 추출하는 동안 End sequence 가 오는 경우는 의미 없는 Tracking 정보라고 생각을 하고 보내지 않는다.
		boolean finishCheckFlag = false;
		
		if(mPendingRequests != null && mPendingRequests.size() > 0) {
			for(int i = 0; i < mPendingRequests.size(); i++) {
				String request = mPendingRequests.get(i);
				
				if(request.contains("end_time=")) {
					finishCheckFlag = true;
					break;
				}
			}
		}
		
		if(!finishCheckFlag) {
			BKLog.e("Extract adid finished. send start tag.");
			String url = addDefaultParam(REQUEST_APP_START);
			
			// add device name, package name, vendor, location, start time second, gmt offset
			url = url + "&start_time=" + String.valueOf(mStartTimeSec) + "&gmt_offset=" + CommonUtils.getCurrentGmt() + "&content_type=json";
			
			BKLog.d("Send Start Tag : " + url);
			mConnManager.sendRequest(REQUEST_APP_START, url, BKConnectionManager.HTTP_POST);
		} else {
			BKLog.e("mIsEndTagCalled true");
			// 20141120 google ad id를 구하는 도중 end tag가 전송된 경우 모든 요청전송을 취소한다.
			if(mPendingRequests != null) {
				mPendingRequests.clear();
			} else {
				mPendingRequests = new ArrayList<String>();
			}
		}
		
	}
	
	private void sendEndInfo() {
		// TODO : 끝날 때 보낼 내용, 끝나는 시간의 현재 time sec, play time 서버에 전송
		mEndTimeSec = System.currentTimeMillis() / 1000;
		double playTime = (mEndTimeSec - mStartTimeSec);
		
		if(mStartTimeSec > 0 && playTime >= 0) {
			String url = addDefaultParam(REQUEST_APP_FINISH);
			
			// 1. end time second 추가, 2. play time 추가 3. GMT offset 추가
			url = url + "&end_time=" + String.valueOf(mEndTimeSec) + "&play_time=" + String.valueOf(playTime) + "&gmt_offset=" + CommonUtils.getCurrentGmt();
			
			/*
			 * for test
			 * */
			url += "&start_time=" + mStartTimeSec;
			
			BKLog.d("Send End Tag : " + url);
			
			if(mIsStartTagSended) {
				mConnManager.sendRequest(REQUEST_APP_FINISH, url, BKConnectionManager.HTTP_POST);
				Toast.makeText(mContext, "finish tag 전송.(session : " + mSessionId + ")", Toast.LENGTH_SHORT).show();
			} else {
				if(mPendingRequests == null) {
					mPendingRequests = new ArrayList<String>();
				}
				mPendingRequests.add(url);
				BKLog.d("pending finish tag - start tag not sended");
			}
		}
		// ad id 관련 변수 초기화
		mIsAdIdAvailable = false;
		mAdId = UNKNOWN;
		
		// 20141121 EndInfo를 보낸 후 udtID 관련 변수도 초기화 - udt를 기존에 초기화시 가져오던 것을 onStart에서 가져오는 것으로 변경함.
		mUdtId = UNKNOWN;
	}
	
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	private String addDefaultParam(int request) {
		// 모든 Tag에 매번 포함되는 parameter를 분여준다.
		// 1. app id 2. device id(UDID) 3. App version 4. OS version 5. network env(wifi or 3g) 6. IP
		// 신규추가(20140729) 7. advertising id 8. uniq device tracking id(udt id)
		String url = getBaseUrl(request);
		if(mTrackingInfo != null) {
			boolean hasParameter = url.contains("?");
			
			if(!hasParameter) {
				url = url + "?";
			} else {
				url = url + "&";
			}
			String appId = "";
			if(mAppId == null || mAppId.equals("")) {
				appId = "UNKNOWN";
			} else {
				appId = mAppId;
			}
			
			checkInstallReferrer();
			
			// 1. Add appID, udid, app version, os version, network state, ip, ad id, udt id
			url = url + "app_id=" + appId + "&udid=" + mTrackingInfo.getUdid() + "&app_ver=" + mTrackingInfo.getAppVersion() + 
				"&os_ver=" + mTrackingInfo.getAndroidVersion() + "&network_state=" + mTrackingInfo.getNetworkState() + "&ip=" + mTrackingInfo.getIP() +
				"&ad_id=" + mAdId + "&udt_id=" + mUdtId + "&device_name=" + mTrackingInfo.getDeviceName() + "&package_name=" + mTrackingInfo.getPackageName() + "&provider=" + mTrackingInfo.getServiceProvider()
				+ "&country=" + mTrackingInfo.getCountryCode() + "&install_referrer=" + mInstallReferrer + "&debug=" + (mIsDebugMode? "1" : "0");
			
			if(request != REQUEST_APP_START) {
				url = url + "&session_id=" + mSessionId;
			}
			
			if(request == REQUEST_PAGE_CHANGE) {
				BKLog.e("UDT_ID : " + mUdtId);
				BKLog.e("Install referrer : " + mInstallReferrer);
			}
		}
		return url;
	}
	
	/**
	 * Request state listener
	 */
	
	@Override
	public void onRequestFinished(int whichRequest, HttpResponse result, String extractString) {
		switch(whichRequest) {
			case REQUEST_APP_START:
				Toast.makeText(mContext, "App start tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("start tag send success");
				try {
					JSONObject responseString = new JSONObject(extractString);
					JSONObject response = responseString.getJSONObject("response");
					mSessionId = response.getString("session_id");
					BKLog.i("-------- received session id : " + mSessionId);
					if(mUdtId.equals("") || mUdtId.equals("UNKNOWN")) {
						String udtId = response.getString("udt_id");
						if(udtId != null && !udtId.equals("")) {
							mUdtId = udtId;
							saveUdtId(mUdtId);
						}
						
						BKLog.i("-------- received udt id : " + mUdtId);
					}
					
					
				} catch(JSONException e) {
					e.printStackTrace();
				}
				mIsStartTagSended = true;
				sendPendingRequest();
				
				break;
				
			case REQUEST_APP_FINISH:
				Toast.makeText(mContext, "App finish tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("finish tag send success");
				initializeVariables();
				break;
				
			case REQUEST_PURCHASE:
				Toast.makeText(mContext, "Purchase tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("purchase tag send success");
				break;
				
			case REQUEST_PAGE_CHANGE:
				Toast.makeText(mContext, "Page tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("page change tag send success");
				break;
				
			case REQUEST_WEB_TRACKING:
				Toast.makeText(mContext, "webView tracking send success(same as page change tag)", Toast.LENGTH_SHORT).show();
				BKLog.d("webView tracking send success(same as page change tag)");
				break;
				
		}
	}

	@Override
	public void onRequestError(int whichRequest, int errorCode, String message) {
		switch(whichRequest) {
			case REQUEST_APP_START:
				BKLog.e("start tag send fail");
				Toast.makeText(mContext, "App start tag send fail", Toast.LENGTH_SHORT).show();
				mIsStartTagSended = true;
				sendPendingRequest();
				break;
				
			case REQUEST_APP_FINISH:
				BKLog.e("finish tag send fail");
				Toast.makeText(mContext, "App finish tag send fail", Toast.LENGTH_SHORT).show();
				initializeVariables();
				break;
				
			case REQUEST_PURCHASE:
				BKLog.e("purchase tag send fail");
				Toast.makeText(mContext, "Purchase tag send fail", Toast.LENGTH_SHORT).show();
				break;
				
			case REQUEST_PAGE_CHANGE:
				BKLog.e("page change tag send fail");
				Toast.makeText(mContext, "Page tag send fail", Toast.LENGTH_SHORT).show();
				break;
				
			case REQUEST_WEB_TRACKING:
				Toast.makeText(mContext, "webView tracking send fail(same as page change tag)", Toast.LENGTH_SHORT).show();
				BKLog.d("webView tracking send fail(same as page change tag)");
				break;
		}
	}
}
